/*
 * Copyright 1999-2022 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.nacos.plugin.datasource.impl.oracle;

import com.alibaba.nacos.common.utils.ArrayUtils;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.NamespaceUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.datasource.constants.ContextConstant;
import com.alibaba.nacos.plugin.datasource.constants.DataSourceConstant;
import com.alibaba.nacos.plugin.datasource.constants.FieldConstant;
import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper;
import com.alibaba.nacos.plugin.datasource.model.MapperContext;
import com.alibaba.nacos.plugin.datasource.model.MapperResult;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * The mysql implementation of ConfigInfoMapper.
 *
 * @author hyx
 **/

public class ConfigInfoMapperByOracle extends AbstractMapperByOracle implements ConfigInfoMapper {

    private static final String DATA_ID = "dataId";

    private static final String GROUP = "group";

    private static final String APP_NAME = "appName";

    private static final String CONTENT = "content";

    private static final String TENANT = "tenant";

    @Override
    public MapperResult findConfigInfoByAppFetchRows(MapperContext context) {
        final String appName = (String) context.getWhereParameter(FieldConstant.APP_NAME);
        final String tenantId = (String) context.getWhereParameter(FieldConstant.TENANT_ID);
        int startRow = context.getStartRow();
        int endRow = startRow + context.getPageSize();

        String sql = "SELECT id, data_id, group_id, tenant_id, app_name, content FROM ("
                + "    SELECT id, data_id, group_id, tenant_id, app_name, content, ROW_NUMBER() OVER (ORDER BY id) AS rn "
                + "    FROM config_info "
                + "    WHERE tenant_id LIKE ? AND app_name = ? "
                + ") WHERE rn BETWEEN " + (startRow + 1) + " AND " + endRow;

        return new MapperResult(sql, CollectionUtils.list(tenantId, appName));
    }

    @Override
    public MapperResult getTenantIdList(MapperContext context) {
        int startRow = context.getStartRow();
        int endRow = startRow + context.getPageSize();

        String sql = "SELECT tenant_id FROM ("
                + "    SELECT tenant_id, ROW_NUMBER() OVER (ORDER BY tenant_id) AS rn "
                + "    FROM config_info "
                + "    WHERE tenant_id != '" + NamespaceUtil.getNamespaceDefaultId() + "' "
                + "    GROUP BY tenant_id "
                + ") WHERE rn BETWEEN " + (startRow + 1) + " AND " + endRow;

        return new MapperResult(sql, Collections.emptyList());
    }

    @Override
    public MapperResult getGroupIdList(MapperContext context) {
        int startRow = context.getStartRow();
        int endRow = startRow + context.getPageSize();

        String sql = "SELECT group_id FROM ("
                + "    SELECT group_id, ROW_NUMBER() OVER (ORDER BY group_id) AS rn "
                + "    FROM config_info "
                + "    WHERE tenant_id = '" + NamespaceUtil.getNamespaceDefaultId() + "' "
                + "    GROUP BY group_id "
                + ") WHERE rn BETWEEN " + (startRow + 1) + " AND " + endRow;

        return new MapperResult(sql, Collections.emptyList());
    }

    @Override
    public MapperResult findAllConfigKey(MapperContext context) {
        int startRow = context.getStartRow();
        int endRow = startRow + context.getPageSize();
        String tenantId = (String) context.getWhereParameter(FieldConstant.TENANT_ID);

        String sql = "SELECT data_id, group_id, app_name FROM ("
                + "    SELECT data_id, group_id, app_name, ROW_NUMBER() OVER (ORDER BY id) AS rn "
                + "    FROM config_info "
                + "    WHERE tenant_id LIKE ? "
                + ") WHERE rn BETWEEN " + (startRow + 1) + " AND " + endRow;

        return new MapperResult(sql, CollectionUtils.list(tenantId));
    }

    @Override
    public MapperResult findAllConfigInfoBaseFetchRows(MapperContext context) {
        int startRow = context.getStartRow() + 1;
        int endRow = context.getStartRow() + context.getPageSize();
        String sql = "SELECT t.id, data_id, group_id, content, md5 "
                + "FROM (SELECT id FROM (SELECT id, ROWNUM rn FROM config_info ORDER BY id) "
                + "WHERE rn BETWEEN " + startRow + " AND " + endRow + ") g, "
                + "config_info t WHERE g.id = t.id";
        return new MapperResult(sql, Collections.emptyList());
    }

    @Override
    public MapperResult findAllConfigInfoFragment(MapperContext context) {
        String contextParameter = context.getContextParameter(ContextConstant.NEED_CONTENT);
        boolean needContent = Boolean.parseBoolean(contextParameter);
        final String sql = "SELECT * FROM (SELECT id, data_id, group_id, tenant_id, app_name, "
                + (needContent ? "content, " : "")
                + "md5, gmt_modified, type, encrypted_data_key, ROWNUM rn FROM config_info WHERE id > ? ORDER BY id ASC) WHERE rn BETWEEN ? AND ?";
        List<Object> paramList = new ArrayList<>();
        paramList.add(context.getWhereParameter(FieldConstant.ID));
        paramList.add(context.getStartRow() + 1);
        paramList.add(context.getStartRow() + context.getPageSize());

        return new MapperResult(sql, paramList);
    }

    @Override
    public MapperResult findChangeConfigFetchRows(MapperContext context) {
        final String tenant = (String) context.getWhereParameter(FieldConstant.TENANT_ID);
        final String dataId = (String) context.getWhereParameter(FieldConstant.DATA_ID);
        final String group = (String) context.getWhereParameter(FieldConstant.GROUP_ID);
        final String appName = (String) context.getWhereParameter(FieldConstant.APP_NAME);
        final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
        final Timestamp startTime = (Timestamp) context.getWhereParameter(FieldConstant.START_TIME);
        final Timestamp endTime = (Timestamp) context.getWhereParameter(FieldConstant.END_TIME);

        List<Object> paramList = new ArrayList<>();

        final String sqlFetchRows = "SELECT * FROM (SELECT a.*, ROWNUM rn FROM (SELECT id, data_id, group_id, "
                + "tenant_id, app_name, type, md5, gmt_modified FROM config_info WHERE ";
        String where = " 1=1 ";

        if (!StringUtils.isBlank(dataId)) {
            where += " AND data_id LIKE ? ";
            paramList.add(dataId);
        }
        if (!StringUtils.isBlank(group)) {
            where += " AND group_id LIKE ? ";
            paramList.add(group);
        }
        if (!StringUtils.isBlank(tenantTmp)) {
            where += " AND tenant_id = ? ";
            paramList.add(tenantTmp);
        }
        if (!StringUtils.isBlank(appName)) {
            where += " AND app_name = ? ";
            paramList.add(appName);
        }
        if (startTime != null) {
            where += " AND gmt_modified >= ? ";
            paramList.add(startTime);
        }
        if (endTime != null) {
            where += " AND gmt_modified <= ? ";
            paramList.add(endTime);
        }

        int startRow = context.getStartRow();
        int pageSize = context.getPageSize();

        final String sql = sqlFetchRows + where + " AND id > ? ORDER BY id ASC) a WHERE ROWNUM <= ?) WHERE rn > ?";
        paramList.add(context.getWhereParameter(FieldConstant.LAST_MAX_ID));
        paramList.add(startRow + pageSize);
        paramList.add(startRow);
        return new MapperResult(sql, paramList);
    }

    @Override
    public MapperResult listGroupKeyMd5ByPageFetchRows(MapperContext context) {
        int startRow = context.getStartRow();
        int pageSize = context.getPageSize();

        // Oracle 查询使用 ROW_NUMBER() 进行分页
        String sql = "SELECT t.id, data_id, group_id, tenant_id, app_name, md5, type, gmt_modified, encrypted_data_key FROM ("
                + "    SELECT id, data_id, group_id, tenant_id, app_name, md5, type, gmt_modified, encrypted_data_key, "
                + "           ROW_NUMBER() OVER (ORDER BY id) AS rn "
                + "    FROM config_info "
                + ") t WHERE rn BETWEEN " + (startRow + 1) + " AND " + (startRow + pageSize);

        return new MapperResult(sql, Collections.emptyList());
    }

    @Override
    public MapperResult findConfigInfoBaseLikeFetchRows(MapperContext context) {
        final String dataId = (String) context.getWhereParameter(FieldConstant.DATA_ID);
        final String group = (String) context.getWhereParameter(FieldConstant.GROUP_ID);
        final String content = (String) context.getWhereParameter(FieldConstant.CONTENT);

        final StringBuilder sqlFetchRows = new StringBuilder("SELECT id, data_id, group_id, tenant_id, content FROM (")
                .append(" SELECT id, data_id, group_id, tenant_id, content, ROW_NUMBER() OVER (ORDER BY id) AS rn ")
                .append(" FROM config_info WHERE tenant_id = '").append(NamespaceUtil.getNamespaceDefaultId()).append("' ");

        final List<Object> paramList = new ArrayList<>();

        if (!StringUtils.isBlank(dataId)) {
            sqlFetchRows.append(" AND data_id LIKE ? ");
            paramList.add(dataId);
        }
        if (!StringUtils.isBlank(group)) {
            sqlFetchRows.append(" AND group_id LIKE ? ");
            paramList.add(group);
        }
        if (!StringUtils.isBlank(content)) {
            sqlFetchRows.append(" AND content LIKE ? ");
            paramList.add(content);
        }

        sqlFetchRows.append(") WHERE rn BETWEEN ").append(context.getStartRow() + 1).append(" AND ")
                .append(context.getStartRow() + context.getPageSize());

        return new MapperResult(sqlFetchRows.toString(), paramList);
    }

    @Override
    public MapperResult findConfigInfo4PageFetchRows(MapperContext context) {
        final String tenant = (String) context.getWhereParameter(FieldConstant.TENANT_ID);
        final String dataId = (String) context.getWhereParameter(FieldConstant.DATA_ID);
        final String group = (String) context.getWhereParameter(FieldConstant.GROUP_ID);
        final String appName = (String) context.getWhereParameter(FieldConstant.APP_NAME);
        final String content = (String) context.getWhereParameter(FieldConstant.CONTENT);

        List<Object> paramList = new ArrayList<>();

        String sql = "SELECT id, data_id, group_id, tenant_id, app_name, content, type, encrypted_data_key FROM ("
                + " SELECT id, data_id, group_id, tenant_id, app_name, content, type, encrypted_data_key, "
                + " ROW_NUMBER() OVER (ORDER BY id) AS rn "
                + " FROM config_info WHERE tenant_id = ? ";

        // 添加 tenant 参数
        paramList.add(tenant);

        // 添加其他查询条件
        if (StringUtils.isNotBlank(dataId)) {
            sql += " AND data_id = ? ";
            paramList.add(dataId);
        }
        if (StringUtils.isNotBlank(group)) {
            sql += " AND group_id = ? ";
            paramList.add(group);
        }
        if (StringUtils.isNotBlank(appName)) {
            sql += " AND app_name = ? ";
            paramList.add(appName);
        }
        if (!StringUtils.isBlank(content)) {
            sql += " AND content LIKE ? ";
            paramList.add(content);
        }

        // 添加分页条件
        sql += ") WHERE rn BETWEEN " + (context.getStartRow() + 1) + " AND "
                + (context.getStartRow() + context.getPageSize());

        return new MapperResult(sql, paramList);
    }

    @Override
    public MapperResult findConfigInfoBaseByGroupFetchRows(MapperContext context) {
        String sql = "SELECT id, data_id, group_id, content FROM ("
                + " SELECT id, data_id, group_id, content, ROW_NUMBER() OVER (ORDER BY id) AS rn "
                + " FROM config_info "
                + " WHERE group_id = ? AND tenant_id = ?"
                + ") WHERE rn BETWEEN " + (context.getStartRow() + 1) + " AND "
                + (context.getStartRow() + context.getPageSize());

        return new MapperResult(sql, CollectionUtils.list(
                context.getWhereParameter(FieldConstant.GROUP_ID),
                context.getWhereParameter(FieldConstant.TENANT_ID)
        ));
    }

    @Override
    public MapperResult findConfigInfoLike4PageFetchRows(MapperContext context) {
        final String tenant = (String) context.getWhereParameter(FieldConstant.TENANT_ID);
        final String dataId = (String) context.getWhereParameter(FieldConstant.DATA_ID);
        final String group = (String) context.getWhereParameter(FieldConstant.GROUP_ID);
        final String appName = (String) context.getWhereParameter(FieldConstant.APP_NAME);
        final String content = (String) context.getWhereParameter(FieldConstant.CONTENT);
        final String[] types = (String[]) context.getWhereParameter(FieldConstant.TYPE);

        String sql = "SELECT * FROM ( "
                + "SELECT id, data_id, group_id, tenant_id, app_name, content, encrypted_data_key, type, "
                + "ROW_NUMBER() OVER (ORDER BY id) AS rn "
                + "FROM config_info WHERE 1=1 ";

        List<Object> params = new ArrayList<>();

        if (StringUtils.isNotBlank(tenant)) {
            sql += " AND tenant_id LIKE ? ";
            params.add(tenant);
        }
        if (StringUtils.isNotBlank(dataId)) {
            sql += " AND data_id LIKE ? ";
            params.add(dataId);
        }
        if (StringUtils.isNotBlank(group)) {
            sql += " AND group_id LIKE ? ";
            params.add(group);
        }
        if (StringUtils.isNotBlank(appName)) {
            sql += " AND app_name = ? ";
            params.add(appName);
        }
        if (StringUtils.isNotBlank(content)) {
            sql += " AND content LIKE ? ";
            params.add(content);
        }
        if (!ArrayUtils.isEmpty(types)) {
            sql += " AND type IN (" + String.join(", ", Collections.nCopies(types.length, "?")) + ") ";
            Collections.addAll(params, types);
        }

        sql += ") WHERE rn BETWEEN " + (context.getStartRow() + 1) + " AND " + (context.getStartRow() + context.getPageSize());
        return new MapperResult(sql, params);
    }

    @Override
    public MapperResult findAllConfigInfoFetchRows(MapperContext context) {
        String sql = "SELECT t.id, data_id, group_id, tenant_id, app_name, content, md5 FROM ("
                + " SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS rn "
                + "  FROM config_info "
                + "  WHERE tenant_id LIKE ? ) g "
                + "JOIN config_info t ON g.id = t.id "
                + "WHERE rn BETWEEN ? AND ?";

        // Oracle的分页从1开始
        int startRow = context.getStartRow() + 1;
        int endRow = startRow + context.getPageSize() - 1;

        return new MapperResult(sql,
                CollectionUtils.list(context.getWhereParameter(FieldConstant.TENANT_ID), startRow, endRow));
    }

    @Override
    public String getDataSource() {
        return DataSourceConstant.ORACLE;
    }

}
